﻿////////////////////////////////////////////////////////////////////////////////////////////
// File: Gamepad.cs                                                                       //
// Author: Mike Binnix                                                                    //
// Desc: Class representing a Xbox 360 controller.  Wraps the XNA input methods for       //
//             gamepads into a single class to more easily check a users input.           //
////////////////////////////////////////////////////////////////////////////////////////////

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace SlothEngine.Input
{
    public class XNAGamepad : IInputDevice
    {
        private GamePadState currentState;
        private GamePadState previousState;
        private PlayerID player;
        private bool initialized;

        public PlayerID Player
        {
            get
            {
                return player;
            }
            set
            {
                if (value == PlayerID.All)
                    throw new Exception("Can not assign PlayerID to PlayerID." + value.ToString());
                else
                    player = value;
            }
        }
        public bool Initialized
        {
            get
            {
                return initialized;
            }
        }
        public bool Connected
        {
            get
            {
                return currentState.IsConnected;
            }
        }

        public XNAGamepad()
        {
            player = PlayerID.One;

            GamePadState currentState = GamePad.GetState((PlayerIndex)player);
            GamePadState previousState = GamePad.GetState((PlayerIndex)player);

            initialized = true;
        }
        public XNAGamepad(PlayerID player)
        {
            if (player == PlayerID.All)
                throw new Exception("Can not assign PlayerID to PlayerID." + player.ToString());
            else
                this.player = player;

            GamePadState currentState = GamePad.GetState((PlayerIndex)player);
            GamePadState previousState = GamePad.GetState((PlayerIndex)player);

            initialized = true;
        }

        /// <summary>
        /// Updates the state for the gamepad.
        /// </summary>
        /// <param name="gameTime">Current game time.</param>
        public void Update(GameTime gameTime)
        {
            previousState = currentState;
            currentState = GamePad.GetState((PlayerIndex)player);
        }
        /// <summary>
        /// Checks if a button on the gamepad has been pressed in the current frame.
        /// </summary>
        /// <param name="button">Button to check.</param>
        /// <returns>Returns true if button has been pressed this frame.  Otherwise returns false.</returns>
        public bool ButtonPressed(Buttons button)
        {
            if (currentState.IsButtonDown(button) && previousState.IsButtonUp(button))
                return true;
            else
                return false;
        }
        /// <summary>
        /// Checks if a button on the gamepad has been held for multiple frames.
        /// </summary>
        /// <param name="button">Button to check.</param>
        /// <returns>Returns true if button has been held over multiple frames.  Otherwise returns false.</returns>
        public bool ButtonHeld(Buttons button)
        {
            if (currentState.IsButtonDown(button) && previousState.IsButtonDown(button))
                return true;
            else
                return false;
        }
        /// <summary>
        /// Checks if a button on the gamepad has been released on current frame.
        /// </summary>
        /// <param name="button">Button to check against.</param>
        /// <returns>Returns true if button was released.  Otherwise returns false.</returns>
        public bool ButtonReleased(Buttons button)
        {
            if (currentState.IsButtonUp(button) && previousState.IsButtonDown(button))
                return true;
            else
                return false;
        }
        /// <summary>
        /// Vibrates the controller by the given amount for each motor.
        /// </summary>
        /// <param name="leftAmount">How intense the vibrating should be for the low-frequency motor.</param>
        /// <param name="rightAmount">How intense the vibrating should be for the high-frequency motor.</param>
        public void Vibrate(float leftAmount, float rightAmount)
        {
            GamePad.SetVibration((PlayerIndex)player, leftAmount, rightAmount);
        }
        /// <summary>
        /// Returns the specified position of a given thumbstick.
        /// </summary>
        /// <param name="stick">Which sticks position to return.</param>
        /// <returns>Returns the thumbsticks position as a vector2.</returns>
        public Vector2 ThumbstickPosition(ThumbStick stick)
        {
            if (stick == ThumbStick.Left)
                return currentState.ThumbSticks.Left;
            else
                return currentState.ThumbSticks.Right;
        }
        /// <summary>
        /// Returns the angle that a thumbstick is pressed in.
        /// </summary>
        /// <param name="stick">Stick to use.</param>
        /// <returns>Angle in radians that the thumbstick is pressed.  Returns float.NaN if thumbstick is not pressed in a direction</returns>
        public float ThumbstickAngle(ThumbStick stick)
        {
            Vector2 position = Vector2.Zero;

            if (stick == ThumbStick.Left)
            {
                position.X = currentState.ThumbSticks.Left.X;
                position.Y = currentState.ThumbSticks.Left.Y;
            }
            else
            {
                position.X = currentState.ThumbSticks.Right.X;
                position.Y = currentState.ThumbSticks.Right.Y;
            }

            if (position == Vector2.Zero)
            {
                return float.NaN;
            }

            return (float)Math.Atan2(position.X, position.Y);
        }
        /// <summary>
        /// Returns the amount that the specified trigger is pushed in.
        /// </summary>
        /// <param name="trigger">Which trigger to return.</param>
        /// <returns>Returns the amount that the tirgger is pushed in. 0.0f is not pressed, 1.0f is fully pressed.</returns>
        public float TriggerValue(Trigger trigger)
        {
            if (trigger == Trigger.Left)
                return currentState.Triggers.Left;
            else
                return currentState.Triggers.Right;
        }
    }
}
